Skip to content

Release 2.0.16: backport channelData optional-subfield fix (#564) - #566

Merged
Corina (corinagum) merged 2 commits into
release/v2.0from
cg/release-2.0.16
Aug 19, 2026
Merged

Release 2.0.16: backport channelData optional-subfield fix (#564)#566
Corina (corinagum) merged 2 commits into
release/v2.0from
cg/release-2.0.16

Conversation

@corinagum

Copy link
Copy Markdown
Contributor

Hotfix release 2.0.16 on the pre-2.1 train.

What

Backports #564 ("Make channelData subfields optional to avoid throw", fixes #563) onto release/v2.0, and sets the stable version to 2.0.16.

The Teams service can send nested channelData objects as empty ({}). Because AppInfo.id was required, Pydantic raised a ValidationError and ActivityTypeAdapter.validate_python has no fallback, so the whole activity was rejected and affected bots silently stopped responding.

This was a regression from #504, which first shipped in 2.0.15 — so release/v2.0 is directly affected.

Commits

  • Make channelData subfields optional to avoid throw (#564) — clean git cherry-pick -x of 5fcd8fa from main, zero conflicts
  • Set stable version 2.0.16version.json 2.0.15 -> 2.0.16

Fix-only: no other main commits since 2.0.15 are included.

Validation

Run locally on this branch:

  • pytest packages788 passed
  • pyright0 errors, 0 warnings
  • ruff checkAll checks passed
  • nbgv get-version — version core 2.0.16 (the -g<sha> suffix is expected off a publicReleaseRefSpec branch and resolves to plain 2.0.16 once merged into release/v2.0)

ruff format reports drift in examples/formatted-messaging/src/main.py and packages/api/.../quoted_reply_entity.py. Both are pre-existing on release/v2.0 (they fail at base commit f01b31f too) and untouched by this change, so theyre left alone.

Merge instructions

Important

Merge with a merge commit, not squash — matching the #546 / #497 precedent on this branch.

After merge: run the ADO publish pipeline on release/v2.0 with Publish Type = Public (ESRP -> PyPI as 2.0.16, dist-tag latest), then tag and draft the GitHub release.

Corina (corinagum) and others added 2 commits August 19, 2026 11:03
Fixes #563

The Teams service can send nested `channelData` objects as empty objects
( `{}` ). Because `AppInfo.id` was required, Pydantic raised a
`ValidationError`, and since `ActivityTypeAdapter.validate_python` has
no fallback, the entire activity was rejected, so affected bots silently
stopped responding.

Reported against 2.0.15, the first release containing #504. Confirmed to
affect customers, currently in 1:1 chats, with exposure growing as an
upstream server-side rollout ramps.

## Root cause
`channelData.app` is optional, so an absent `app` was always fine, but a
present but empty one wasn't. Auditing the model graph showed this isn't
unique to `app`: 98 sites exist where an optional parent points at a
child with required fields. Seven were reachable from a plain message
activity:
```
channelData.app={}       -> channelData.app.id            Field required
channelData.channel={}   -> channelData.channel.id        Field required
channelData.team={}      -> channelData.team.id           Field required
channelData.tenant={}    -> channelData.tenant.id         Field required
channelData.settings={}  -> channelData.settings.selectedChannel
attachments=[{}]         -> attachments.0.contentType
entities=[{}]            -> entities.0._unknown.type
```

## Change
Make these inbound-only fields optional: `AppInfo.id`, `ChannelInfo.id`,
`TeamInfo.id`, `TenantInfo.id`, `ChannelDataSettings.selected_channel`,
`Attachment.content_type`, `EntityBase.type`.

`MessageUpdateChannelData` and `MessageDeleteChannelData` both inherit
`ChannelData`, so they're covered without separate edits.

`EntityBase.type` is relaxed at the base rather than overridden on
`UnknownEntity`: every concrete entity already overrides `type` with a
`Literal` default, so `UnknownEntity` is the only class inheriting it
raw. This also means an entity type introduced after this SDK version no
longer drops the message.

## Tests
New `test_empty_inbound_objects.py` covering each empty-object case, an
unrecognized entity type, that populated values still parse, and that
absent `channelData` is unchanged.

## Validation
- 852 tests pass across `packages/api` + `packages/apps` 
- `ruff format`  / `ruff check`  clean
- pyright: zero net new errors

## Notes

This is the defensive half. The empty object is a service-side
wire-contract issue and has been raised with that team separately; this
change means the SDK degrades gracefully regardless.

(cherry picked from commit 5fcd8fa)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@corinagum
Corina (corinagum) merged commit 1f67bfa into release/v2.0 Aug 19, 2026
5 checks passed
@corinagum
Corina (corinagum) deleted the cg/release-2.0.16 branch August 19, 2026 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants